package com.syl.starter.itools.framework.utils;

import com.google.common.base.Splitter;
import com.syl.starter.itools.common.utils.StringUtils;
import lombok.NoArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * @author syl
 * @create 2018-08-10 16:01
 **/
@Component
@NoArgsConstructor
public class HttpUtils {
    private static final Logger LOG = LoggerFactory.getLogger(HttpUtils.class);

    @Autowired
    private RestTemplate restTemplate;
    /**
     * 内部api调用所需要的特殊请求头 一般用来突破登录
     */
    private Map<String, String> interiorHeader;

    public HttpUtils(Map<String, String> interiorHeader){
        this.interiorHeader = interiorHeader;
    }

//    /**
//     * 内部api 调用get请求
//     * @param url
//     * @param params
//     * @return string 结果
//     */
//    public String interiorApiGetForString(String url, Map<String, String> params){
//        return interiorApiGetForObject(url, params, String.class);
//    }
//
//    public String interiorApiGetForString(String url, Object javaBean){
//        return interiorApiForString(url, JsonUtils.toJson(javaBean), HttpMethod.GET);
//    }
//
//    /**
//     * 内部api 调用get请求 突破登录
//     * @param url
//     * @param params
//     * @param responseType
//     *
//     * @param <T>
//     * @return
//     */
//    public <T> T interiorApiGetForObject(String url, Map<String, String> params, Class<T> responseType){
//        return interiorApiForObject(url, null, params, HttpMethod.GET, responseType);
//    }
//
//    public <T> T interiorApiGetForObject(String url, Object javaBean, Class<T> responseType){
//        return interiorApiForObject(url, JsonUtils.toJson(javaBean), null, HttpMethod.GET, responseType);
//    }
//
//    /**
//     * 内部api 调用请求 突破登录 最简单形式
//     * @param url
//     * @param type
//     * @return
//     */
//    public String interiorApiForString(String url, HttpMethod type){
//        return interiorApiForObject(url, null, null, type, String.class);
//    }
//
//    /**
//     * 内部api 调用请求 突破登录 无json
//     * @param url
//     * @param params
//     * @param type
//     * @return
//     */
//    public String interiorApiForString(String url, Map<String, String> params, HttpMethod type){
//        return interiorApiForObject(url, null, params, type, String.class);
//    }
//
//    /**
//     * 内部api 调用请求 突破登录
//     * @param url
//     * @param json
//     * @param params
//     * @param type
//     *
//     * @return 字符串
//     */
//    public String interiorApiForString(String url, String json, Map<String, String> params, HttpMethod type){
//        return interiorApiForObject(url, json, params, type, String.class);
//    }
//
//    public String interiorApiForString(String url, String json, HttpMethod type){
//        return interiorApiForObject(url, json, null, type, String.class);
//    }
//
//    /**
//     * 内部api 调用请求 突破登录
//     * @param url
//     * @param json 发送json数据 注意：如果是get请求 那么接收的名称必须为bean
//     * @param params
//     * @param type
//     *
//     * @return
//     */
//     public <T> T interiorApiForObject(String url, String json, Map<String, String> params, HttpMethod type, Class<T> responseType){
//        Map<String, String> header = interiorHeader;
//        if(HttpMethod.GET == type){
//            if(StringUtils.isNotEmpty(json))
//                url = url + "?bean=" + new URLEncoder().encode(json, Charset.forName(BaseConstant.UTF8));
//            return getForObject(url, params, header, responseType);
//        }
//        if(HttpMethod.POST == type){
//            if(StringUtils.isNotEmpty(json))
//                return postJsonForObject(url, json, params, header, responseType);
//            return postForObject(url, params, header, responseType);
//        }
//        if(HttpMethod.PUT == type){
//            if(StringUtils.isNotEmpty(json))
//                return exchangeJsonForObject(url, HttpMethod.PUT, json, params, header, responseType);
//            return exchangeForObject(url, HttpMethod.PUT, params, header, responseType);
//        }
//        if(HttpMethod.DELETE == type){
//            if(StringUtils.isNotEmpty(json))
//                return exchangeJsonForObject(url, HttpMethod.DELETE, json, params, header, responseType);
//            return exchangeForObject(url, HttpMethod.DELETE,  params, header, responseType);
//        }
//        return null;
//    }

    /**
     * 发送get请求
     * @param url
     * @param params
     * @param header
     * @return
     * @throws RestClientException
     */
    public String getForString(String url, Map<String, String> params, Map<String, String> header) throws RestClientException {
        return getForString(url, params, getHeader(header));
    }

    /**
     * 发送get请求
     * @param url
     * @param params
     * @param header
     * @return
     * @throws RestClientException
     */
    public String getForString(String url, Map<String, String> params, HttpHeaders header) throws RestClientException {
        return getForString(url,params, new HttpEntity<>(header));
    }

    /**
     * 发送get请求
     * @param url
     * @param params
     * @param header
     * @return
     * @throws RestClientException
     */
    public String getForString(String url, Map<String, String> params, HttpEntity<?> header) throws RestClientException {
        return getForString(packUrlParamString(url, params), header);
    }

    /**
     * 以get请求获取内容
     * @param url
     * @param header
     * @return
     */
    public String getForString(String url, HttpEntity<?> header) throws RestClientException {
        return getForObject(url, header, String.class);
    }

    public <T> T getForObject(String url, Map<String, String> params, Class<T> responseType) throws RestClientException {
        return getForObject(url, params, null, responseType);
    }

    public <T> T getForObject(String url, Map<String, String> params, Map<String, String> header, Class<T> responseType) throws RestClientException {
        return getForObject(packUrlParamString(url, params), new HttpEntity<>(getHeader(header)), responseType);
    }

    public <T> T getForObject(String url, HttpEntity<?> header, Class<T> responseType) throws RestClientException {
        if(header == null)
            return restTemplate.getForObject(url, responseType);
        ResponseEntity<T> entity = restTemplate.exchange(url, HttpMethod.GET, header, responseType);
        return entity.getBody();
    }

    /**
     * 以post请求获取内容 发送json
     * @param url
     * @param json
     * @return
     */
    public String postJsonForString(String url, String json){
        return postJsonForString(url, json, null);
    }

    /**
     * 以post请求获取内容 发送json
     * @param url
     * @param json
     * @param header 请求头
     * @return
     */
    public String postJsonForString(String url, String json, Map<String, String> header){
        return postJsonForString(url, json, null, header);
    }

    /**
     * 以post请求获取内容 发送json和参数
     * @param url
     * @param json
     * @param param url参数
     * @param header 请求头
     *
     * @return
     */
    public String postJsonForString(String url, String json, Map<String, String> param, Map<String, String> header){
        return postJsonForObject(url, json, param, header, String.class);
    }

    public <T> T postJsonForObject(String url, String json, Class<T> responseType){
        return postJsonForObject(url, json, null, responseType);
    }

    public <T> T postJsonForObject(String url, String json, Map<String, String> param, Class<T> responseType){
        return postJsonForObject(url, json, param, null, responseType);
    }

    public <T> T postJsonForObject(String url, String json, Map<String, String> param, Map<String, String> header, Class<T> responseType){
        HttpHeaders headers = getHeader(header);
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpEntity<String> entity = new HttpEntity(json, headers);
        // 这里在url 后面做拼接 和get 一样处理
        String newUrl = packUrlParamString(url, param);
        return restTemplate.postForObject(newUrl, entity, responseType);
    }

    /**
     * 以post请求获取字符串内容
     * @param url
     * @param param
     * @param headers
     *
     * @return
     */
    public String postForString(String url, Map<String, String> param, Map<String, String> headers){
        return postForString(url, param, headers, String.class);
    }

    /**
     * 以post请求获取字符串内容
     * @param url
     * @param param
     * @param headers
     * @param responseType 返回类型
     *
     * @return 返回类型的数据
     */
    public <T> T postForString(String url, Map<String, String> param, Map<String, String> headers, Class<T> responseType){
        Class cz = responseType == null ? String.class : responseType;
        return (T) postForObject(url, param, headers, cz);
    }

    /**
     * 以post请求获取内容 发送表单数据
     * @param url url
     * @param param 请求参数
     * @param responseType 返回结果类型
     * @param headers 请求头
     * @return
     */
    public <T> T postForObject(String url, Map<String, String> param, Map<String, String> headers, Class<T> responseType){
        return postForObject(url, param, getHeader(headers), responseType);
    }

    /**
     * 以post请求获取内容 发送表单数据
     * @param url url
     * @param param 请求参数
     * @param responseType 返回结果类型
     * @param headers 请求头
     * @return
     */
    public <T> T postForObject(String url, Map<String, String> param, HttpHeaders headers, Class<T> responseType) throws RestClientException {
        headers = headers == null ? new HttpHeaders() : headers;
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(packParam(param), headers);
        return restTemplate.postForObject(url, request, responseType);
    }

    /**
     * 简便发送/获取数据
     * @param url
     * @param method
     * @param param
     * @param headers
     * @return
     */
    public <T> T exchangeForObject(String url, HttpMethod method, Map<String, String> param, Map<String, String> headers, Class<T> responseType){
        return exchange(url, method, null,  param, headers, responseType);
    }

    /**
     * 简便发送/获取 不适合于get方式的请求
     * @param url
     * @param method
     * @param json json
     * @param param
     * @param headers
     * @return
     */
    public <T> T exchangeJsonForObject(String url, HttpMethod method, String json, Map<String, String> param, Map<String, String> headers, Class<T> responseType){
        return exchange(url, method, json,  param, headers, responseType);
    }

    /**
     * 文件下载
     * @param folderFile 存放文件夹目录
     * @param urls 下载地址
     * @return
     * @throws IOException
     */
    public int download(String folderFile, String... urls) throws IOException {
        return this.download(folderFile, false, urls);
    }

    /**
     * 文件下载
     * @param folderFile 存放文件夹目录
     * @param randomName 是否保存为随机名称
     * @param urls 下载地址
     *
     * @return 成功返回下载数量 失败返回-1
     * @throws IOException
     */
    public int download(String folderFile, boolean randomName, String... urls) throws IOException {
        if (StringUtils.isAnyEmpty(folderFile)) return -1;
        if (urls.length < 1) return -1;
        HttpHeaders headers = new HttpHeaders();
        HttpEntity<Resource> httpEntity = new HttpEntity<>(headers);
        int succeed = 1;
        for (String url : urls) {
            ResponseEntity<byte[]> response = restTemplate.exchange(url, HttpMethod.GET, httpEntity, byte[].class);
            String subtype = response.getHeaders().getContentType().getSubtype();
            LOG.debug("===状态码================");
            LOG.debug(">> {}", response.getStatusCodeValue());
            LOG.debug("===返回信息================");
            LOG.debug(">> {}", response.getHeaders().getContentType());
            LOG.debug(">> {}", subtype);
            folderFile = StringUtils.mustLast(folderFile, File.separator);
            File file = new File(folderFile + (randomName ? System.currentTimeMillis() : succeed) + "." + subtype);
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(response.getBody());
            fos.flush();
            fos.close();
            succeed ++;
        }
        return succeed;
    }

    /**
     *  发送/获取 服务端数据
     * @param url
     * @param method
     * @param json
     * @param param
     * @param headers
     * @param responseType
     * @param <T>
     * @return
     */
    public <T> T exchange(String url, HttpMethod method, String json, Map<String, String> param, Map<String, String> headers, Class<T> responseType) {
        return exchange(url, method, json, null, param, headers, responseType);
    }

    /**
     *  发送/获取 服务端数据(主要用于解决发送put,delete方法无返回值问题).
     * @param url
     * @param method 请求类型
     * @param json json body 可以为空
     * @param pathVariable rest url变量 /{xx} 可以为空
     * @param param url参数 可以为空
     * @param headers 请求头 可以为空
     * @param tClass 返回的数据类型
     * @param <T>
     * @return
     */
    public <T> T exchange(String url, HttpMethod method, String json, Map<String, String> pathVariable, Map<String, String> param, Map<String, String> headers, Class<T> tClass) {
        HttpHeaders header = getHeader(headers);
        HttpEntity entity;
        if(StringUtils.isNotEmpty(json)) {
            header.setContentType(MediaType.APPLICATION_JSON);
            entity = new HttpEntity(json, header);
        }else
            entity = new HttpEntity<>(header);
        if(pathVariable == null)
            pathVariable = new HashMap<>();
        url = packUrlParamString(url, param);
        ResponseEntity responseEntity = restTemplate.exchange(url, method, entity, tClass, pathVariable);
        return (T) responseEntity.getBody();
    }

    /**
     * 从url中提取参数 默认是无法改变的map
     * @param url
     * @return
     */
    public static Map<String, String> getUrlParam(String url){
        return getUrlParam(url, false);
    }

    /**
     * 从url中提取参数
     * @param url
     * @param change 是否可改变的
     * @return
     */
    public static Map<String, String> getUrlParam(String url, boolean change){
        String params = url.substring(url.indexOf("?") + 1);
        Map<String, String> split = Splitter.on("&").withKeyValueSeparator("=").split(params);
        if(change) return new HashMap<>(split);
        return split;
    }

    /**
     * 包装url 参数
     * @param url
     * @param params 纯string类型map
     * @return
     */
    private String packUrlParamString(String url, Map<String, String> params){
        StringBuffer sb = new StringBuffer(url);
        if(params == null)
            return sb.toString();
        Set<String> keySet = params.keySet();
        Iterator<String> iterator = keySet.iterator();
        int i = 0;
        while (iterator.hasNext()){
            String key = iterator.next();
            if(i == 0)
                sb.append("?").append(key).append("=").append(params.get(key));
            else
                sb.append("&").append(key).append("=").append(params.get(key));
            i++;
        }
        return sb.toString();
    }

    /**
     * 包装请求参数 非GET 可使用
     * @param param
     * @return
     */
    private MultiValueMap packParam(Map<String, String> param){
        MultiValueMap<String, String> map = new LinkedMultiValueMap();
        if(param == null)
            return map;
        Set<String> keySet = param.keySet();
        for (String key : keySet) {
            map.add(key,param.get(key));
        }
        return map;
    }

    /**
     * 获取 Header 包装类
     * @param headerMap
     * @return
     */
    private HttpHeaders getHeader(Map<String, String> headerMap){
        HttpHeaders headers = new HttpHeaders();
        if(headerMap == null)
            return headers;
        for (String key : headerMap.keySet()) {
            headers.set(key, headerMap.get(key));
        }
        return headers;
    }

}
